/*
 * Copyright 2009 Bart Van Dosselaer
 * 
 * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.intrepid.oss.hudson.core.service;

import java.io.IOException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.intrepid.oss.hudson.core.domain.EntryStatus;
import org.intrepid.oss.hudson.core.domain.Feed;
import org.intrepid.oss.hudson.core.domain.FeedEntry;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * @author Bart Van Dosselaer
 */
public class FeedServiceImpl implements FeedService {

	private static final XPathExpression X_ID;
	private static final XPathExpression X_LINK;
	private static final XPathExpression X_TITLE;

	private static final Pattern P_SUCCESS = Pattern.compile("\\(SUCCESS\\)$");
	private static final Pattern P_FAILURE = Pattern.compile("\\(FAILURE\\)$");
	private static final Pattern P_UNSTABLE = Pattern.compile("\\(UNSTABLE\\)$");
	
	private static final Pattern P_TITLE = Pattern.compile("((.)+) #\\d+ \\(\\w+\\)");
	
	static {
		final XPathFactory xPathFactory = XPathFactory.newInstance();
		final XPath xPath = xPathFactory.newXPath();
		
		try {
			X_ID = xPath.compile("id");
			X_LINK = xPath.compile("link/@href");
			X_TITLE = xPath.compile("title");
		} catch (XPathExpressionException e) {
			throw new RuntimeException(e.getLocalizedMessage(), e);
		}
	}
	
	/**
	 * @see org.intrepid.oss.hudson.core.service.FeedService#readFeed(java.net.URL)
	 */
	@Override
	public Feed readFeed(final URL url) throws IOException {
		if(url == null) {
			throw new IOException("error.url.invalid");
		}
		final Document document = parseURL(url);
		return parseDocument(document);
	}
	
	private Document parseURL(final URL url) throws IOException {
		try {
			final DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();

			return documentBuilder.parse(url.openStream());
		} catch (ParserConfigurationException e) {
			throw new IOException("error.url.parser", e);
		} catch (SAXException e) {
			throw new IOException("error.url.parser", e);
		}
	}
	
	private Feed parseDocument(final Document document) throws IOException {
		final FeedEntry feedInformation = fetchFeedEntry(document.getDocumentElement(), false);
		
		final NodeList nodeList = document.getElementsByTagName("entry");
		final int n = nodeList.getLength();
		final Set<FeedEntry> entries = new HashSet<FeedEntry>(n);
		
		for(int i = 0; i < n; i++) {
			final Node node = nodeList.item(i);
			entries.add(fetchFeedEntry((Element)node, true));
		}
		
		return new Feed(feedInformation, entries);
	}


	private FeedEntry fetchFeedEntry(final Element element, final boolean parseTitle) throws IOException {
		try {
			final String id = FeedServiceImpl.X_ID.evaluate(element); 
			final String title = FeedServiceImpl.X_TITLE.evaluate(element); 
			final String link = FeedServiceImpl.X_LINK.evaluate(element); 
			return new FeedEntry(id, parseTitle ? fetchTitle(title) : title, link, fetchStatus(title));
		} catch (XPathExpressionException e) {
			// very little chance of this happening!
			throw new IOException(e.getMessage(), e);
		}
	}
	
	protected String fetchTitle(final String title) {
		final Matcher matcher = FeedServiceImpl.P_TITLE.matcher(title);
		
		if(matcher.find()) {
			return matcher.group(1);
		}
		
		return title;
	}
	
	private EntryStatus fetchStatus(final String value) {
		if(P_SUCCESS.matcher(value).find()) {
			return EntryStatus.SUCCESS;
		}

		if(P_FAILURE.matcher(value).find()) {
			return EntryStatus.FAILURE;
		}

		if(P_UNSTABLE.matcher(value).find()) {
			return EntryStatus.UNSTABLE;
		}
		
		return EntryStatus.UNKNOWN;
	}
	
}
